package com.ssky.bean.validator.proxy;

import com.ssky.bean.validator.core.IFullFieldValueGetter;
import com.ssky.bean.validator.core.VldConstants;
import com.ssky.bean.validator.core.rule.Field;
import com.ssky.bean.validator.util.DefinerUtils;
import javassist.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 根据bean，生成内部属性getter代理获取方法；如果属性类型为collection、map、array类型，则跳过该类型及子属性的获取
 *
 * @Author: ssky
 * @Date: 2021/07/21
 * @Description: 创建IFullFieldValueGetter的实现类
 */
public class FullFieldValueGetterCreator {
    public static final Logger L = LoggerFactory.getLogger(FullFieldValueGetterCreator.class);

    private static final String NLine = "\n";
    private static final String START_CLASS_NAME = "ffvg$";
    private static final String ParamName = "bean";
    private static final ClassPath classPath = new ClassClassPath(IFullFieldValueGetter.class);
    private static final String PackageStr = DynProxyImplPackagePosition.class.getPackage().getName();

    //fieldFullname,fullFieldValueGetter
    private final Map<String, IFullFieldValueGetter> fullFieldValueGetterMap;
    private Class<?> beanClass;

    public FullFieldValueGetterCreator() {
        this.fullFieldValueGetterMap = new HashMap<>();
    }

    public Map<String, IFullFieldValueGetter> createAll(Class<?> beanClass) throws Exception {
        this.beanClass = beanClass;

        Field beanField = new Field();
        beanField.setPreField(null);
        beanField.setClassType(beanClass);
        this.process(beanField, null);
        return this.fullFieldValueGetterMap;
    }

    private void process(Field fieldV, String preFullName) throws Exception {
        java.lang.reflect.Field[] rftFields = fieldV.getClassType().getDeclaredFields();
        String thisFullName = "";
        for (java.lang.reflect.Field fieldRft : rftFields) {
            String fieldName = fieldRft.getName();
            int typeFlag = fieldRft.getModifiers();
            if (Modifier.isStatic(typeFlag)) {
                continue;
            }
            if (preFullName == null || preFullName == "") {
                thisFullName = fieldRft.getName();
            } else {
                thisFullName = preFullName + "." + fieldName;
            }
            Field fv = new Field();
            fv.setName(fieldName);
            fv.setPreField(fieldV);
            fv.setClassType(fieldRft.getType());

            IFullFieldValueGetter fullFieldGetter = create(fv);
            this.fullFieldValueGetterMap.put(thisFullName, fullFieldGetter);
            if (!DefinerUtils.typeIsSupportGet(fieldRft.getType())) {//不能从collect、map、array数组等结构中取值
                continue;
            }

            if (!DefinerUtils.isJavaClass(fieldRft.getType())) {
                process(fv, thisFullName);
            }
        }
    }

    /**
     * @param field
     * @return
     * @throws Exception
     */
    //   Object getV( Object obj);
    private IFullFieldValueGetter create(Field field) throws Exception {

        CtClass impCtClass = null;
        ClassPool pool = new ClassPool(true);
        pool.insertClassPath(classPath);
        CtClass ctInterface = pool.getCtClass(IFullFieldValueGetter.class.getName());
        impCtClass = pool.makeClass(PackageStr + "." + START_CLASS_NAME + field.getName() + VldConstants.Atomic_Long.getAndIncrement());
        impCtClass.addInterface(ctInterface);

        String methodStr = genMethodStr(field);
        CtMethod method = null;
        try {
            method = CtNewMethod.make(methodStr, impCtClass);
            impCtClass.addMethod(method);
        } catch (CannotCompileException e) {
            L.error("impCtClass.addMethod(method) occur exception ,methodStr --->" + methodStr, e);
            throw e;
        }
        Class<IFullFieldValueGetter> clazz = (Class<IFullFieldValueGetter>) impCtClass.toClass(ValidatorClassLoader.getCustomCLoader(FullFieldValueGetterCreator.class));
        // todo   清除javassist缓存
        IFullFieldValueGetter ist = clazz.newInstance();
        return clazz.newInstance();
    }

    private String genMethodStr(Field fieldV) {
        String methodStr = "public Object " + IFullFieldValueGetter.Method_Name + " ( Object " + ParamName + ")";
        StringBuilder body = new StringBuilder(400);
        body.append("{").append(NLine);
        body.append(beanClass.getName() + " " + "b" + " = (" + beanClass.getName() + ")" + ParamName + ";").append(NLine);//声明bean

        String getFvStr = "";
        List<Field> fieldLs = DefinerUtils.toFullFieldList(fieldV);
        for (int i = 0; i < fieldLs.size(); i++) {
            Field field = fieldLs.get(i);
            if (i == 0) {
                getFvStr = getFvStr + DefinerUtils.genBeanGetMethodName(field)+"()";
            } else {
                getFvStr = getFvStr + "." + DefinerUtils.genBeanGetMethodName(field)+"()";
            }
            if (i != fieldLs.size() - 1) {
                body.append("if(b." + getFvStr + "==null){ return null; }").append(NLine);
            } else if (DefinerUtils.isPrimitive(field.getClassType())) {
                body.append("return new " + DefinerUtils.getBoxType(field.getClassType()) + "( b." + getFvStr + ");").append(NLine);
            } else {
                body.append("return b." + getFvStr + ";").append(NLine);
            }
        }
        body.append("}").append(NLine);
        methodStr = methodStr + body.toString();
        return methodStr;
    }


}
